package com.gallop.mq.annotation;


import com.gallop.mq.MQProperties;
import com.gallop.mq.core.MQListener;
import com.gallop.mq.core.dispatcher.DispatchRpcCallable;
import com.gallop.mq.core.MQListenerInterceptor;

import java.lang.annotation.*;

/**
 * 需要接收MQ监听的接口或实现类，在对应接收方法上标注此注解。
 * <p>
 * 在方法上使用：被注解方法所在的对象必须是一个 spring-bean；方法签名至少需要一个参数，用来接收MQ消息。
 * <p>
 * 在类上使用：被注解类必须实现接口{@link MQListener}；
 * <h3>监听组</h3>
 * 关心同一{@link MQMessageListener#topic}的容器都会收到监听消息；
 * 当定义了监听组{@link MQMessageListener#group}后，同一监听组内只有一个容器（随机选择）会收到消息。
 * <h3>rpc模式</h3>
 * <p>
 * 被注解的方法支持响应rpc模式，目前有三种使用方式：
 *
 * <li>1. 在参数列表中定义：
 * <p>
 * 可另外在被注解的监听方法签名中声明一个{@link  DispatchRpcCallable}参数，用于处理rpc监听，
 * 参数会被自动创建并注入，顺序无特别要求。
 * <pre>
 *     &#64;MQMessageListener(topic="foo.bar.request")
 *     public void foo(FoobarMessage message, DispatchRpcCallable rpcCallback) {
 *         // ...
 *         FoobarResponse someResponse;
 *         // 响应调用方
 *         rpcCallback.reply(someResponse);
 *     }
 * </pre>
 * <p>
 * 此时在监听方法内，
 * 回调参数rpcCallback需要由响应方自行决定何时调用{@link  DispatchRpcCallable#reply(Object)}
 * 方法的触发，以响应调用方；
 * </li>
 * <p>
 *
 * <li>2. 通过返回值定义响应类型：
 * <p>
 * 也可以在监听方法签名中定义非void返回值，返回值可以是约定中要响应调用方的类型，也可以是一个{@link  DispatchRpcCallable}；
 * <pre>
 *      &#64;MQMessageListener(topic="foo.bar.request")
 *      public FoobarResponse foo(FoobarMessage message) {
 *          //...
 *      }
 *  </pre>
 * <p>
 * 此时当监听方法调用完成后，MQ组件会使用返回值作为响应消息，自动触发响应；
 * </li>
 * <p>
 * <li>3. 通过返回值定义回调参数：
 * <p>
 * 和上面的例子类似，此时返回值是一个{@link  DispatchRpcCallable}，需要监听方法内自行创建；
 * <pre>
 *       &#64;MQMessageListener(topic="foo.bar.request")
 *       public DispatchRpcCallable foo(FoobarMessage message) {
 *           //...
 *           DispatchRpcCallable callback = DispatchRpcCallable.builder()
 *           .replyTo("xx.xx.xxx")
 *           .rpcResponse((replyTopic,response)->{
 *               // ...
 *           })
 *           .build();
 *       }
 *   </pre>
 * </li>
 * <p>
 * rpc模式的响应超时时间可以通过{@link MQProperties#getRequestTimeout()}设置，单位ms
 * <p>
 * 如果调用方使用了非rpc方式发送消息，rpc模式则会变成普通的订阅监听，相关参数仍会被注入但响应不会被触发；
 *
 * @see DispatchRpcCallable
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MQMessageListener {

    /**
     * 监听的主题
     */
    String topic() default "";

    /**
     * 监听分组，同一组只有一个监听会消费消息
     */
    String group() default "";

    Class<? extends MQListenerInterceptor>[] interceptor() default {};
}
